﻿using System;
using System.Collections.Generic;

namespace System
{
	public delegate void TreeNodeEvent<T>(TreeNode<T> P, int Depth);
	public class TreeNode<T>
	{
		public String Name;
		public T Content;
		public TreeNode<T> Left, Right, Parent;
		public List<TreeNode<T>> Children = new List<TreeNode<T>>();
		public TreeNode<T> this[int N]
		{
			get
			{
				return Children[N];
			}
		}
		public void LinkChildren()
		{
			for (int i = 0; i < Children.Count; i++)
			{
				Children[i].Left = i == 0 ? null : Children[i - 1];
				Children[i].Right = i < Children.Count - 1 ? Children[i + 1] : null;
				Children[i].Parent = this;
			}
		}
		public void AddNode(TreeNode<T> S)
		{
			Children.Add(S);
			if (Children.Count > 1)
			{
				S.Left = Children[Children.Count - 2];
				Children[Children.Count - 2].Right = S;
			}
			S.Parent = this;
		}
		public TreeNode<T> AddNode(String N, T obj)
		{
			TreeNode<T> Res = new TreeNode<T>(N, obj);
			AddNode(Res);
			return Res;
		}
		public void AddSibling(TreeNode<T> S)
		{
			Parent.AddNode(S);
		}
		public void AddSibling(String N, T obj)
		{
			AddSibling(new TreeNode<T>(N, obj));
		}

		public TreeNode(String N)
		{
			Name = N;
		}
		public TreeNode(String N,T obj)
		{
			Name = N;
			Content = obj;
		}

		public void Traverse(TreeNodeEvent<T> Action)
		{
			TreeNode<T> Current = this;
			int Depth = 0;
			while (Current != null)
			{
				Action(Current, Depth);
				if (Current.Children.Count > 0)
				{
					Current = Current.Children[0];
					Depth++;
				}
				else
				{
					while (Current != null && Current.Right == null)
					{
						Current = Current.Parent;
						Depth--;
					}
					if (Current != null)
						Current = Current.Right;
				}
			}
		}
	}
	public class ProgressTreeNode : TreeNode<double>
	{
		public double Progress;
		public double Weight;
		public ProgressTreeNode(String N,double prg, double w)
			: base(N,prg)
		{
			Progress = -1;
			Weight = w;
		}
		new private TreeNode<double> AddNode(double obj)
		{
			throw new Exception("Hidden");
		}
		public void AddNode(ProgressTreeNode N)
		{
			base.AddNode(N);
		}
		public ProgressTreeNode AddNode(String N, double prg, double w)
		{
			ProgressTreeNode res = new ProgressTreeNode(N, prg, w);
			AddNode(res);
			return res;
		}

		public double CalculateProgress()
		{
			if (Children.Count == 0)
				return Progress = Content;

			double W = 0;
			double P = 0;
			foreach (ProgressTreeNode CH in Children)
			{
				P += CH.CalculateProgress() * CH.Weight;
				W += CH.Weight;
			}
			Progress = P / W;
			return Progress;
		}

		#region Console Output
		static ConsoleColor ProgressColor(double prg)
		{
			if (prg == 0)
				return ConsoleColor.DarkRed;
			if (prg < 0.25)
				return ConsoleColor.Red;
			if (prg < 0.75)
				return ConsoleColor.Yellow;
			if (prg < 1)
				return ConsoleColor.Cyan;
			return ConsoleColor.Green;
		}
		void WriteConsole(double newparam, params double[] parentprogresses)
		{
			double[] X = new double[parentprogresses.Length + 1];
			Array.Copy(parentprogresses, X, X.Length - 1);
			X[X.Length - 1] = newparam;
			WriteConsole(X);
		}
		public void WriteConsole(params double[] parentprogresses)
		{
			for (int i = 0; i < parentprogresses.Length; i++)
			{
				Console.BackgroundColor = ProgressColor(parentprogresses[i]);
				Console.Write("  ");
			}
			Console.ResetColor();
			Console.WriteLine((Progress * 100).ToString("000.00") + "%");
			foreach (var CH in Children)
				(CH as ProgressTreeNode).WriteConsole(Progress, parentprogresses);
		}
		#endregion

		public ProgressTreeNode[] Leaves()
		{
			List<ProgressTreeNode> Res = new List<ProgressTreeNode>();
			Traverse((X, D) =>
			{
				if (X.Children.Count == 0)
					Res.Add(X as ProgressTreeNode);
			});
			return Res.ToArray();
		}
	}
}	